package keyword

import (
	"fmt"
)

/*
	前言:
		当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子、那么这只鸟就可以被 称为鸭子。
		---- 詹姆斯▪惠特科姆▪莱利
	说明:
		go通过接口实现了类安全的鸭子类型，同时又避免了oop编程中的继承。我们来学习go的接口(interface),go的接口是一种抽象的自定义类型，
		没办法直接实例化，它声明了一个或者多个方法的签名。如果一个结构体(struct)实现了一个接口定义的所有方法,我们就说这个结构体(struct)实现了这个接口
		。
	注意：
		这里说的『实现』是隐式的。你不用显示声明某个struct实现了那个接口。
	命名规则:
		go喜欢用er给接口作为一个后缀,比如(Reader/Writer)
	获取传入接口是那种结构体(struct)类型:
		go提供了一种叫做类型断言的来获取具体的结构体类型。语法比较简单,格式如下:
		instance,ok := interfaceVal.(RealType) // 如果ok为true的话，接口就转成我们想要的类型
	空接口类型:
		所有类型都实现了空接口(interface),所以可以使用空接口+类型断言的方式转成我们需要的类型。
*/

// 定义一个睡觉的接口

type Sleeper interface {
	Sleep() // 实现了一个睡觉的方法
}

// 定义一个吃饭的接口

type Eater interface {
	Eat(foodList []string) // 实现了吃饭的方法
}

// 定义一个懒的动物的接口.

type LazyAnimal interface {
	Sleeper
	Eater
}

// 定义一个dog 的结构体

type Dog struct {
	Name string
}

// 实现一个睡觉的方法
func (d Dog) Sleep() {
	fmt.Println("Dog", d.Name, "is sleeping!")
}

// 实现一个吃饭的方法
func (d Dog) Eat(foodList []string) {
	fmt.Printf("%s is eating %v!\n", d.Name, foodList)
}

// 定义一个cat 结构体

type Cat struct {
	Name string
}

// 实现一个睡觉的方法

func (c Cat) Sleep() {
	fmt.Println("Cat", c.Name, "is sleeping!")
}

// 实现一个吃饭的方法

func (c Cat) Eat(foodList []string) {
	fmt.Printf("%s is eating %v!\n", c.Name, foodList)
}

// 定义一个动物睡觉的方法
func AnimalSleep(s Sleeper) {
	s.Sleep()
}

// 接口主执行方法
//func InterfaceVariableV2() {
//	var s Sleeper
//	dog := Dog{Name: "Snoopy"}
//	cat := Cat{Name: "HelloKity"}
//	// 使用接口进行赋值
//	s = dog
//	AnimalSleep(s)
//	fmt.Println("--------------------")
//	s = cat
//	AnimalSleep(s)
//	fmt.Println("--------------------")
//	// 定义一个切片
//	sleepList := []Sleeper{Dog{"Snoopy"}, Cat{"HelloKity"}}
//	for _, s := range sleepList {
//		s.Sleep()
//	}
//}

func mPrint(params interface{}) {
	switch p := params.(type) {
	case int:
		fmt.Printf("%d\n", p)
	case string:
		fmt.Printf("%s\n", p)
	case float64:
		fmt.Printf("%f\n", p)
	default:
		fmt.Printf("%+v\n", p)
	}
}

// 接口主执行方法
func InterfaceVariableV2() {
	sleeperList := []LazyAnimal{Cat{Name: "HelloKity"}, Dog{Name: "Snoopy"}}
	foodList := []string{"鲱鱼罐头", "榴莲", "臭豆腐", "螺蛳粉", "蓝纹奶酪", "豆汁", "崂山白花蛇草水"}
	for _, s := range sleeperList {
		s.Eat(foodList)
		s.Sleep()

		// 类型断言 types assert
		if dog, ok := s.(Dog); ok {
			fmt.Printf("i am a dog,my name is %s!\n", dog.Name)
		}
		if cat, ok := s.(Cat); ok {
			fmt.Printf("i am a cat,my name is %s!\n ", cat.Name)
		}
	}
	// 通过空接口来将参数转成任何类型
	mPrint(1)
	mPrint(3.1415926)
	mPrint("ikun")
	mPrint(map[string]bool{"ikun": true})
}
